package com.chujian.framework.util;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author ：yj
 * @date ：2020-11-16
 * apache HttpUtil 封装
 */
@Slf4j
public class HttpUs {

    private static CloseableHttpClient httpClient;

    static {
        try {
            // 信任所有
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (x509Certificates, s) -> true).build();

            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", sslsf)
                    .build();

            ManagedHttpClientConnectionFactory connFactory = new ManagedHttpClientConnectionFactory(
                    DefaultHttpRequestWriterFactory.INSTANCE,
                    DefaultHttpResponseParserFactory.INSTANCE
            );
            SystemDefaultDnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;

            // 连接池管理
            PoolingHttpClientConnectionManager clientConnectionManager = new PoolingHttpClientConnectionManager(
                    socketFactoryRegistry,
                    connFactory,
                    dnsResolver
            );

            // Socket 配置
            SocketConfig socketConfig = SocketConfig.custom()
                    .setTcpNoDelay(true)
                    .build();

            clientConnectionManager.setDefaultSocketConfig(socketConfig);
            // 配置永久连接的最大总数或每个路由限制
            // 可以保留在池中或由连接管理器租用。
            // 每个路由的默认最大连接，每个路由实际最大连接为默认为DefaultMaxPreRoute控制，而MaxTotal是控制整个池子最大数
            clientConnectionManager.setMaxTotal(5000);
            clientConnectionManager.setDefaultMaxPerRoute(50);
            // 在从连接池获取连接时，连接不活跃多长时间后需要进行一次验证，默认为2s
            clientConnectionManager.setValidateAfterInactivity(1000 * 5);

            RequestConfig defaultRequestConfig = RequestConfig.custom()
                    .setConnectTimeout(3 * 1000) //设置连接超时时间 3s
                    .setSocketTimeout(30 * 1000) //设置等待数据超时时间，30s
                    .setConnectionRequestTimeout(2000) //设置从连接池获取连接的等待超时时间
                    .build();

            httpClient = HttpClients.custom()
                    .setConnectionManager(clientConnectionManager)
                    .setDefaultRequestConfig(defaultRequestConfig)
                    .setConnectionManagerShared(false) //连接池不是共享模式
                    .evictIdleConnections(120, TimeUnit.SECONDS) //定期回收空闲连接
                    .evictExpiredConnections()  //定期回收空闲连接
                    .setConnectionTimeToLive(60, TimeUnit.SECONDS) //连接存活时间，如果不设置，则根据长连接信息决定
                    .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) //连接重用策略，即是否能keeplive
                    .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE) //长连接配置，即获取长连接生产多长时间
                    .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)) //设置重试次数，默认为3次;当前禁用掉(根据需要重启)
                    .build();

        } catch (Exception e) {
            log.error("HttpClient初始化失败", e);
        }
    }

    public static CloseableHttpClient httpClient() {
        return httpClient;
    }

    private void get_____________() {
    }

    /**
     * Get请求返回字符串
     *
     * @param baseUrl
     * @return
     */
    public static String httpGetStr(String baseUrl) {
        return httpGetStr(baseUrl, null);
    }

    /**
     * Get请求返回字符串
     *
     * @param baseUrl
     * @param params
     * @return
     */
    public static String httpGetStr(String baseUrl, Map<String, String> params) {
        HttpResponse response = httpGet(baseUrl, params);
        return getHttpResponseStr(response);
    }

    /**
     * Get请求返回HttpResponse
     *
     * @param baseUrl
     * @return
     */
    public static HttpResponse httpGet(String baseUrl) {
        return httpGet(baseUrl, null, null);
    }

    public static HttpResponse httpGet(String baseUrl, Map<String, String> params) {
        return httpGet(baseUrl, params, null);
    }

    public static HttpResponse httpGet(String baseUrl, Map<String, String> params, Map<String, String> headers) {
        String url = concatGetUrl(baseUrl, params);
//        log.info(url);

        HttpRequest httpGet = new HttpGet(url);
        httpGet = setHeaders(httpGet, headers);
        try {
            return httpClient.execute(((HttpGet) httpGet));
        } catch (Exception e) {
            log.error("GET请求失败", e);
            return null;
        }
    }

    private void postJson_____________() {
    }

    /**
     * PostJson 返回字符串
     *
     * @param url
     * @param jsonParams
     * @return
     */
    public static String postJsonStr(String url, Object jsonParams) {
        HttpResponse response = postJson(url, jsonParams, null);
        return getHttpResponseStr(response);
    }

    /**
     * PostJson 返回字符串
     *
     * @param url
     * @param jsonParams
     * @return
     */
    public static HttpResponse postJson(String url, Object jsonParams) {
        return postJson(url, jsonParams, null);
    }

    /**
     * PostJson返回 HttpResponse
     *
     * @param url
     * @param jsonParams
     * @param headers
     * @return HttpResponse
     */
    public static HttpResponse postJson(String url, Object jsonParams, Map<String, String> headers) {
        String jsonData = jsonParams instanceof String ? ((String) jsonParams) : JSON.toJSONString(jsonParams);
        return postJson(url, jsonData, headers);
    }

    public static HttpResponse postJson(String url, String jsonData, Map<String, String> headers) {
        HttpRequest request = new HttpPost(url);
        request = setHeaders(request, headers);

        HttpPost httpPost = (HttpPost) request;
        httpPost.setEntity(new StringEntity(jsonData, ContentType.create("application/json", "UTF-8")));
        try {
            return httpClient.execute(httpPost);
        } catch (IOException e) {
            log.error("postJson失败", e);
            return null;
        }
    }

    private void postForm_____________() {
    }


    private void stream_____________() {
    }

    /**
     * 获取网络资源流
     *
     * @param url
     * @return
     */
    public static InputStream getNetInputStream(String url) {
        HttpGet httpGet = new HttpGet(url);
        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            InputStream content = response.getEntity().getContent();
            return content;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    private void util_____________() {
    }


    public static String concatGetUrl(String baseUrl, Map<String, String> params) {
        if (CollUtil.isEmpty(params)) return baseUrl;
        String ps = concatGetParams(params);
        if (baseUrl.contains("?")) {
            return baseUrl + "&" + ps;
        }
        return baseUrl + "?" + ps;
    }

    public static String concatGetParams(Map<String, String> params) {
        return params.keySet().stream()
                .map(k -> k + "=" + params.get(k))
                .collect(Collectors.joining("&"));
    }

    public static String concatGetObjParams(Map<String, Object> params) {
        return params.keySet().stream()
                .map(k -> k + "=" + params.get(k))
                .collect(Collectors.joining("&"));
    }

    public static HttpRequest setHeaders(HttpRequest request, Map<String, String> headers) {
        if (CollUtil.isEmpty(headers)) return request;
        headers.forEach(request::addHeader);
        return request;
    }

    public static String getHttpResponseStr(HttpResponse response) {
        HttpEntity entity = response.getEntity();
        String str = null;
        try {
            str = EntityUtils.toString(entity, "UTF-8");
//            str = new BasicResponseHandler().handleResponse(response);
        } catch (IOException e) {
            log.error("获取返回结果字符失败", e);
        }
        return str;
    }

}
